#include "EventLoop.h"
#include "Acceptor.h"
#include "TcpConnection.h"
#include <sys/epoll.h>
#include <sys/socket.h>
#include <unistd.h>
#include <iostream>

using std::cout;
using std::cerr;
using std::endl;

EventLoop::EventLoop(Acceptor &acceptor)
: _epfd(createEpollFd())
, _evtList(1024)
, _isLooping(false)
, _acceptor(acceptor)
, _conns()
{
    //获取listenfd，然后监听listenfd
    int listenfd = _acceptor.fd();
    addEpollReadFd(listenfd);
}

EventLoop::~EventLoop()
{
    //shift + k
    close(_epfd);
}

//循环运行与否
void EventLoop::loop()
{
    _isLooping = true;
    while(_isLooping)
    {
        waitEpollFd();
    }
}

void EventLoop::unloop()
{
    _isLooping = false;
}

//封装了epoll_wait函数
void EventLoop::waitEpollFd()
{
    int nready = 0;
    do
    {
        nready = ::epoll_wait(_epfd, &*_evtList.begin()
                              , _evtList.size(), 3000);
    }while(-1 == nready && errno == EINTR);

    if(-1 == nready)
    {
        cerr << "-1 == nready" << endl;
        return;
    }
    else if(0 == nready)
    {
        cout << ">>epoll_wait timeout!!!" << endl;
    }
    else
    {
        //考虑vector的大小，也就是扩容的问题，不然有些文件描述符
        //是存不下的
        if(nready == _evtList.size())
        {
            _evtList.resize(2 * nready);
        }

        for(int idx = 0; idx < nready; ++idx)
        {
            int fd = _evtList[idx].data.fd;
            int listenfd = _acceptor.fd();
            if(fd == listenfd)
            {
                if(_evtList[idx].events & EPOLLIN)
                {
                    //处理新的连接
                    handleNewConnection();
                }
            }
            else
            {
                if(_evtList[idx].events & EPOLLIN)
                {
                    //处理数据的收发
                    handleMessage(fd);
                }
            }
        }
    }
}

//处理新的连接
void EventLoop::handleNewConnection()
{
    //只要connfd是正确的，就说明三次握手建立成功，就可以创建连接了
    int connfd = _acceptor.accept();
    if(-1 == connfd)
    {
        perror("handleNewConnection");
        return;
    }
    //将connfd放在红黑树上进行监听
    addEpollReadFd(connfd);

    //创建连接，并且使用智能指针进行托管
    TcpConnectionPtr con(new TcpConnection(connfd));

    //将文件描述符以及连接的对象放在map中存起来
    /* _conns.insert({connfd, con}); */
    _conns[connfd] = con;
}

//处理老的连接上的数据的收发
void EventLoop::handleMessage(int fd)
{
    //需要通过连接收发数据,如何找到连接的对象呢
    //TcpConnection对象呢
    auto it = _conns.find(fd);
    if(it != _conns.end())
    {
        string msg = it->second->receive();//接收客户端数据
        cout << ">>recv msg from client: " << msg << endl;

        //将数据做处理,处理完毕之后需要将数据发给客户端
        //...处理数据
        it->second->send(msg);
    }
    else
    {
        cout << "该连接不存在，不能进行收发数据" << endl;
        return;
    }
}

//创建epfd，封装了epoll_create
int EventLoop::createEpollFd()
{
    int fd = ::epoll_create(100);
    if(fd < 0)
    {
        perror("createEpollFd");
        return -1;
    }

    return fd;
}

//将文件描述符放在红黑树上进行监听
void EventLoop::addEpollReadFd(int fd)
{
    struct epoll_event evt;
    evt.events = EPOLLIN;//读事件
    evt.data.fd = fd;
    int ret = ::epoll_ctl(_epfd, EPOLL_CTL_ADD, fd, &evt);
    if(ret < 0)
    {
        perror("addEpollReadFd");
        return;
    }
}

//将文件描述符从红黑树上取消监听
void EventLoop::delEpollReadFd(int fd)
{
    struct epoll_event evt;
    evt.events = EPOLLIN;//读事件
    evt.data.fd = fd;
    int ret = ::epoll_ctl(_epfd, EPOLL_CTL_DEL, fd, &evt);
    if(ret < 0)
    {
        perror("delEpollReadFd");
        return;
    }
}
